﻿namespace NKernel.Core.Impl
{
    using System;
    using System.Collections.Generic;

    using Core;

    using log4net;

    using Provider;

    /// <summary>
    /// Description of DefaultKernelImpl.
    /// </summary>
    internal class DefaultKernelImpl : AbstractKernel, DefaultKernelImplMBean
    {
        #region Fields

        private readonly IKernelProvider kernelProvider;
        private readonly ILog logger = LogManager.GetLogger(typeof(DefaultKernelImpl));
        private readonly NetMxContainer netMxContainer;

        private IList<IComponentObject> componentObjects;

        #endregion Fields

        #region Constructors

        public DefaultKernelImpl()
        {
            KernelProperties.Configuration(new ConfigLoader().LoadConfigFile());

            if (kernelProvider == null)
            {
                kernelProvider = KernelProperties.KernelProvider;

                if (kernelProvider == null)
                {
                    throw new  MissingMemberException("Ensure that a default Kernel Provider was configured.");
                }
            }

            netMxContainer = new NetMxContainer();
        }

        public DefaultKernelImpl(IKernelProvider kernelProvider)
        {
            this.kernelProvider = kernelProvider;

            KernelProperties.Configuration(new ConfigLoader().LoadConfigFile());

            if (kernelProvider == null)
            {
                throw new MissingMemberException("Ensure that a default Kernel Provider was instantiated.");
            }

            netMxContainer = new NetMxContainer();
        }

        #endregion Constructors

        #region Methods

        public override KernelResult Cleanup()
        {
            return ComponentLifeCycleHandler.Cleanup(componentObjects);
        }

        public override IList<IComponentObject> GetComponents()
        {
            return componentObjects;
        }

        public override T GetInstance<T>()
        {
            return kernelProvider.GetInstance<T>();
        }

        public override KernelResult Init()
        {
            return ComponentLifeCycleHandler.Init(componentObjects);
        }

        public override KernelResult Load(string[] locations)
        {
            try
            {
                var componentDescriptors = new FileComponentLoader().LoadComponents(locations, new ComponentOverrideDirective(KernelProperties.OverrideDirectiveFile));

                logger.Info(String.Format("Number of component descriptors discovered: {0}",
                    componentDescriptors.Count));

                componentObjects = new ComponentDescriptorInstantiator(kernelProvider).Instantiate(componentDescriptors, this);

                logger.Info(String.Format("Number of component objects discovered: {0}",
                    componentObjects.Count));

                netMxContainer.Register(this);
                netMxContainer.RegisterComponents(componentObjects);

                return new KernelResult(true);
            }
            catch (Exception exception)
            {
                string msg = "Problem encountered during the loading of components from " +
                    String.Join(", ", locations);
                logger.Error(msg, exception);
                return new KernelResult(false, msg);
            }
        }

        public override KernelResult Load()
        {
            return Load(KernelProperties.ComponentLocations);
        }

        public override KernelResult Manage()
        {
            return Manage(KernelProperties.ComponentLocations);
        }

        public override KernelResult Manage(string[] locations)
        {
            logger.Info("Managing the full lifecycle for components located at " + string.Join(
                ", ", locations));

            var result = Load(locations);

            if (result.Success)
            {
                KernelResult resultInit = Init();
                if (resultInit.Success)
                {
                    var resultStart = Start();

                    return resultStart;
                }
                return new KernelResult(false, "Problem initialising the components");
            }

            return new KernelResult(false, "Failure to load the components at the indicated locations");
        }

        public override KernelResult Start()
        {
            return ComponentLifeCycleHandler.Start(componentObjects);
        }

        public override KernelResult Stop()
        {
            return ComponentLifeCycleHandler.Stop(componentObjects);
        }

        #endregion Methods
    }
}